#include <base/buffer.h>
using namespace tbs;

size_t cal_size(CONST size_t &old_cap, CONST size_t &need) {
  size_t r = old_cap;
  if (need < 1024) {
	while (r < need) {
	  r *= 2;
	}
  } else {
	while (r < need) {
	  r += 64;
	}
  }
  return r;
}

buffer::Buffer::Buffer(CONST size_t &init_size) {
  resize(init_size, true);
}

buffer::Buffer::Buffer(CONST void *source, CONST size_t &len, CONST size_t &cap) {
  ASSERT(len > cap, "too small buffer for source data")
  resize(cap, true);
  CONST byte *src = (CONST byte *)source;
  for (size_t i = 0; i < len; i++) {
	_data[i] = *(src + i);
  }
  this->_len = len;
}

void buffer::Buffer::write(CONST void *source, CONST size_t &len, CONST size_t &from) {
  write_f(source, len, from, false);
}

void buffer::Buffer::write_f(CONST void *source, CONST size_t &len, CONST size_t &from, CONST bool &resizeable) {
  size_t ed = from + len;
  ASSERT(!resizeable && ed > _cap, "too large data for buffer size,use resizeable=true to resize on write");
  if (ed > _cap) {
	size_t n = cal_size(_cap, ed);
	resize(n, false);
  }
  _len = _len >= ed ? _len : ed;
  CONST auto *src = (CONST byte *)source;
  for (size_t i = from; i < ed; i++) {
	_data[i] = *src;
	src++;
  }
}

void buffer::Buffer::resize(CONST size_t &size, bool clear) {
  byte *newPtr = new byte[size];
  if (clear) {
	_data.reset(newPtr);
	_len = 0;
	_cap = size;
  } else {
	read<byte>([&newPtr, &size](CONST byte &v, CONST size_t &i, bool &stop) {
	  newPtr[i] = v;
	});
	if (_len >= size) {
	  _len = size;
	}
	_cap = size;
	_data.reset(newPtr);
  }
}

size_t buffer::Buffer::cap() CONST NO_EXCEPT {
  return _cap;
}

size_t buffer::Buffer::len() CONST NO_EXCEPT {
  return _len;
}

buffer::Buffer::Buffer(buffer::Buffer &&other) NO_EXCEPT {
  _data.reset(other._data.release());
  _len = other.len();
  _cap = other.cap();
  other._cap = 0;
  other._len = 0;
}

buffer::Buffer &buffer::Buffer::operator=(buffer::Buffer &&other) NO_EXCEPT {
  _data.reset(other._data.release());
  _len = other.len();
  _cap = other.cap();
  other._cap = 0;
  other._len = 0;
  return *this;
}

buffer::Buffer buffer::Buffer::clone() CONST {
  return buffer::Buffer(_data.get(), _len, _cap);
}

void buffer::Buffer::append(const void *source, const size_t &len) {
  write_f(source, len, this->len(), true);
}